package com.jwcao.common.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * name: reflectUtil description: TODO
 *
 * @author caojingwei date: 2023/3/22 15:25
 */
public abstract class ReflectUtil {

    private static final String GETTER_FORMAT = "get%s";

    private static final String SETTER_FORMAT = "set%s";

    public static String getterName(String name) {

        return String.format(GETTER_FORMAT, name.substring(0, 1).toUpperCase() + name.substring(1));
    }

    public static String setterName(String name) {
        return String.format(SETTER_FORMAT, name.substring(0, 1).toUpperCase() + name.substring(1));
    }

    public static Object getInheritedDeclaredFieldValue(Object obj, Field field)
            throws NoSuchFieldException, IllegalAccessException {
        field.setAccessible(true);
        return field.get(obj);
    }

    public static List<Field> getInheritedDeclaredFields(Class<?> fromClass) {
        return getInheritedDeclaredFields(fromClass, null);
    }

    public static List<Field> getInheritedDeclaredFields(Class<?> fromClass, Class<?> stopWhenClass) {
        return getInheritedDeclaredMeta(fromClass, stopWhenClass, Class::getDeclaredFields);
    }

    public static List<Method> getInheritedDeclaredMethods(Class<?> fromClass) {
        return getInheritedDeclaredMethods(fromClass, null);
    }

    public static List<Method> getInheritedDeclaredMethods(Class<?> fromClass, Class<?> stopWhenClass) {
        return getInheritedDeclaredMeta(fromClass, stopWhenClass, Class::getDeclaredMethods);
    }

    public static <T> List<T> getInheritedDeclaredMeta(Class<?> fromClass, Class<?> stopWhenClass,
                                                       Function<Class<?>, T[]> getFunction) {
        if (stopWhenClass == null) {
            stopWhenClass = Object.class;
        }
        List<T> metaList = new ArrayList<>();
        List<Class<?>> classes = new ArrayList<>();

        Class<?> cls = fromClass;
        do {
            classes.add(cls);
            cls = cls.getSuperclass();
        } while (cls != null && !cls.equals(stopWhenClass));

        for (int i = classes.size() - 1; i >= 0; i--) {
            metaList.addAll(Arrays.asList(getFunction.apply(classes.get(i))));
        }

        return metaList;
    }

    public static Field getInheritedDeclaredField(Class<?> fromClass, String fieldName) throws NoSuchFieldException {
        return getInheritedDeclaredField(fromClass, fieldName, null);
    }

    public static Field getInheritedDeclaredField(Class<?> fromClass, String fieldName, Class<?> stopWhenClass)
            throws NoSuchFieldException {
        if (stopWhenClass == null) {
            stopWhenClass = Object.class;
        }

        Class<?> cls = fromClass;
        do {
            Field field;
            try {
                field = cls.getDeclaredField(fieldName);
                return field;
            } catch (NoSuchFieldException | SecurityException e) {
                // Nothing. We'll try to get field from superclass
            }
            cls = cls.getSuperclass();
        } while (cls != null && !cls.equals(stopWhenClass));

        // If we got here, we'll throw an exception
        throw new NoSuchFieldException(fieldName);
    }

    public static void setInheritedDeclaredFieldValue(Object obj, String fieldName, Object value,
                                                      Class<?> stopWhenClass) throws NoSuchFieldException, IllegalAccessException {
        Field field = getInheritedDeclaredField(obj.getClass(), fieldName, stopWhenClass);
        setInheritedDeclaredFieldValue(obj, field, value);
    }

    public static void setInheritedDeclaredFieldValue(Object obj, Field field, Object value)
            throws NoSuchFieldException, IllegalAccessException {
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static boolean isAbstractOrInterface(Class<?> clazz) {
        return Modifier.isAbstract(clazz.getModifiers()) || Modifier.isInterface(clazz.getModifiers());
    }

//    public static <T extends Annotation> List<T> getInheritedDeclaredAnnotation(Class<?> clazz, Class<T> annotation) {
//        List<T> annotationList = new ArrayList<>();
//        T[] annotations = clazz.getAnnotationsByType(annotation);
//        if (ArrayUtils.isNotEmpty(annotations)) {
//            annotationList.addAll(List.of(annotations));
//        }
//        T[] declaredAnnotations = clazz.getDeclaredAnnotationsByType(annotation);
//        if (ArrayUtils.isNotEmpty(declaredAnnotations)) {
//            annotationList.addAll(List.of(declaredAnnotations));
//        }
//        return annotationList;
//    }

}
